namespace SimpleWebServices.Web.Routing
{
    using System;
    using System.Web;
    using System.Web.Mvc;
    using System.Web.Routing;
    using SimpleWebServices.Data;
    using SimpleWebServices.Web.Mvc;

    public sealed class TenantRoute<T> : RouteBase
        where T : IRecord
    {
        public TenantRoute(Type controller, RouteName name)
        {
            if (null == controller)
            {
                throw new ArgumentNullException("controller");
            }
            else if (!typeof(IController).IsAssignableFrom(controller))
            {
                throw new ArgumentOutOfRangeException("controller");
            }
            else if (null == name)
            {
                throw new ArgumentNullException("name");
            }

            this.Controller = controller;
            this.Name = name;
        }

        private Type Controller
        {
            get;
            set;
        }

        private RouteName Name
        {
            get;
            set;
        }

        public override RouteData GetRouteData(HttpContextBase httpContext)
        {
            RouteData result = null;

            Route route = TenantRoute<T>.GetRoute(httpContext, this.Controller, this.Name);
            if (null != route)
            {
                result = route.GetRouteData(httpContext);
            }

            return result;
        }

        public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
        {
            if (null == requestContext)
            {
                throw new ArgumentNullException("requestContext");
            }

            VirtualPathData result = null;

            Route route = TenantRoute<T>.GetRoute(requestContext.HttpContext, this.Controller, this.Name);
            if (null != route)
            {
                result = route.GetVirtualPath(requestContext, values);
            }

            return result;
        }

        private static Route GetRoute(HttpContextBase httpContext, Type controller, RouteName name)
        {
            if (null == httpContext)
            {
                throw new ArgumentNullException("httpContext");
            }

            Route result = null;

            AbsoluteUri urn = new TenantUriConverter(controller, name).Urn;

            if (null != urn)
            {
                if (Record.Exists<T>(urn))
                {
                    HttpRequestBase request = httpContext.Request;
                    result = TenantRoute<T>.New(controller, request.HttpMethod, request.Path.Substring(1));
                }
            }

            return result;
        }

        private static Route New(Type controller, string verb, ResourcePath path)
        {
            if (null == controller)
            {
                throw new ArgumentNullException("controller");
            }

            string language = string.Empty;
            string action = null;

            if (string.IsNullOrEmpty(path.Language))
            {
                if ("POST".Equals(verb, StringComparison.OrdinalIgnoreCase))
                {
                    action = "Post";
                }
                else
                {
                    action = "LanguageNegotiation";
                }
            }
            else
            {
                language = path.Language;
                if (string.IsNullOrEmpty(path.Extension))
                {
                    action = "ContentNegotiation";
                }
                else
                {
                    foreach (var method in controller.GetMethods())
                    {
                        foreach (object attribute in method.GetCustomAttributes(typeof(ContentNegotiationAttribute), true))
                        {
                            var conneg = attribute as ContentNegotiationAttribute;
                            if (conneg.Extension.Equals(path.Extension, StringComparison.OrdinalIgnoreCase))
                            {
                                action = method.Name;
                                break;
                            }
                        }

                        if (null != action)
                        {
                            break;
                        }
                    }
                }
            }

            return new Route(path, new MvcRouteHandler())
            {
                Defaults = new RouteValueDictionary(new 
                {
                    controller = controller.Name.Replace("Controller", string.Empty),
                    action = action,
                    language = language
                }),
                Constraints = new RouteValueDictionary()
            };
        }
    }
}